Rework how we get the event window: don't get the window from the NSEvent,
authorRichard Hult <richard@imendio.com>
Wed, 6 Jun 2007 18:42:07 +0000 (18:42 +0000)
committerRichard Hult <rhult@src.gnome.org>
Wed, 6 Jun 2007 18:42:07 +0000 (18:42 +0000)
2007-06-06  Richard Hult  <richard@imendio.com>

* gdk/quartz/gdkevents-quartz.c (find_mouse_window_for_ns_event)
(find_window_for_ns_event): Rework how we get the event window:
don't get the window from the NSEvent, always use the mouse window
instead. Fix mouse window tracking by only using MouseExited for
non-gdk windows, and to always use the NSEvent window to get the
right gdk window in MouseEntered. Add comments to the code. Fixes bug
#350460.

svn path=/trunk/; revision=18065

ChangeLog
gdk/quartz/gdkevents-quartz.c

index 9b0a0f0ce236824588f7aa6bdac5bc8c7e80374a..8247b9a3614f347d0d7a02f0022ab89e890bc44f 100644 (file)
--- a/ChangeLog
+++ b/ChangeLog
@@ -1,3 +1,13 @@
+2007-06-06  Richard Hult  <richard@imendio.com>
+
+       * gdk/quartz/gdkevents-quartz.c (find_mouse_window_for_ns_event)
+       (find_window_for_ns_event): Rework how we get the event window:
+       don't get the window from the NSEvent, always use the mouse window
+       instead. Fix mouse window tracking by only using MouseExited for
+       non-gdk windows, and to always use the NSEvent window to get the
+       right gdk window in MouseEntered. Add comments to the code. Fixes bug
+       #350460.
+
 2007-06-06  Richard Hult  <richard@imendio.com>
 
        * gdk/quartz/gdkevents-quartz.c (get_converted_window_coordinates):
index 8d0e9439ee76a10d1359697df29cbbb8d6c335ea..536b0c65ec988c08b44219ce0c0adaa92fa762b1 100644 (file)
@@ -937,54 +937,67 @@ get_converted_window_coordinates (GdkWindow *in_window,
                                        out_x, out_y);
 }
 
-/* Given a mouse NSEvent, returns the window in which the pointer
- * position from the event is. The returned coordinates are relative
- * to the found window, and normal GDK coordinates, not Quartz.
- */
+/* Given a mouse NSEvent (must be a mouse event for a GDK window),
+ * finds the subwindow over which the pointer is located. Returns
+ * coordinates relative to the found window. If no window is found,
+ * returns NULL.
+*/
 static GdkWindow *
-find_window_for_mouse_ns_event (NSEvent *nsevent,
+find_mouse_window_for_ns_event (NSEvent *nsevent,
                                 gint    *x_ret,
                                 gint    *y_ret)
 {
-  NSWindow *nswindow;
-  GdkWindow *toplevel;
+  GdkWindow *event_toplevel;
+  GdkWindow *mouse_toplevel;
+  GdkWindow *mouse_window;
   NSPoint point;
   gint x_tmp, y_tmp;
-  GdkWindow *found_window;
-
-  nswindow = [nsevent window];
-  toplevel = [(GdkQuartzView *)[nswindow contentView] gdkWindow];
 
+  event_toplevel = [(GdkQuartzView *)[[nsevent window] contentView] gdkWindow];
   point = [nsevent locationInWindow];
 
   x_tmp = point.x;
 
   /* Flip the y coordinate. */
-  if (toplevel == _gdk_root)
+  if (event_toplevel == _gdk_root)
     y_tmp = _gdk_quartz_window_get_inverted_screen_y (point.y);
   else
     {
       GdkWindowImplQuartz *impl;
 
-      impl = GDK_WINDOW_IMPL_QUARTZ (GDK_WINDOW_OBJECT (toplevel)->impl);
+      impl = GDK_WINDOW_IMPL_QUARTZ (GDK_WINDOW_OBJECT (event_toplevel)->impl);
       y_tmp = impl->height - point.y;
     }
 
-  found_window = _gdk_quartz_window_find_child (toplevel, x_tmp, y_tmp);
+  if (!current_mouse_window)
+    return NULL;
+
+  mouse_toplevel = gdk_window_get_toplevel (current_mouse_window);
 
-  /* Translate the coordinates so they are relative to the found
-   * window.
-   */
-  if (found_window)
-    get_child_coordinates_from_ancestor (toplevel,
+  get_converted_window_coordinates (event_toplevel,
+                                    x_tmp, y_tmp,
+                                    mouse_toplevel,
+                                    &x_tmp, &y_tmp);
+
+  mouse_window = _gdk_quartz_window_find_child (mouse_toplevel, x_tmp, y_tmp);
+  if (!mouse_window)
+    {
+      /* This happens for events on the window title and window
+       * buttons (and the desktop).
+       */
+      return NULL;
+    }
+
+  if (mouse_window != mouse_toplevel)
+    get_child_coordinates_from_ancestor (mouse_toplevel,
                                         x_tmp, y_tmp,
-                                        found_window,
+                                        mouse_window,
                                         &x_tmp, &y_tmp);
 
   *x_ret = x_tmp;
   *y_ret = y_tmp;
 
-  return found_window;
+  return mouse_window;
 }
 
 /* This function finds the correct window to send an event to, taking
@@ -1016,24 +1029,22 @@ find_window_for_ns_event (NSEvent *nsevent,
     {
       GdkWindow *mouse_window;
 
-      mouse_window = find_window_for_mouse_ns_event (nsevent, x, y);
+      mouse_window = find_mouse_window_for_ns_event (nsevent, x, y);
 
-      if (!mouse_window)
-       mouse_window = _gdk_root;
+      /* We don't need to handle the case where we don't find a mouse
+       * window (i.e. after leaving a gdk toplevel and not entering a
+       * new one) here, it's covered by NSMouseExited events.
+       */
+      if (mouse_window && mouse_window != current_mouse_window)
+        synthesize_crossing_events (mouse_window, GDK_CROSSING_NORMAL, nsevent, *x, *y);
 
-      if (_gdk_quartz_pointer_grab_window)
-       {
-         if (mouse_window != current_mouse_window)
-           synthesize_crossing_events (mouse_window, GDK_CROSSING_NORMAL, nsevent, *x, *y);
-       }
-      else
-       {
-         if (current_mouse_window != mouse_window)
-           {
-             synthesize_crossing_events (mouse_window, GDK_CROSSING_NORMAL, nsevent, *x, *y);
-             _gdk_quartz_events_update_cursor (mouse_window);
-           }
-       }
+      /* FIXME: Does this check really work as intended? It seems like
+       * we sometimes get a cursor update when we shouldn't, for
+       * example during an implicit grab on a GtkButton, and dragging
+       * the mouse over a link button.
+       */
+      if (mouse_window && !_gdk_quartz_pointer_grab_window)
+        _gdk_quartz_events_update_cursor (mouse_window);
     }
 
   switch (event_type)
@@ -1070,7 +1081,7 @@ find_window_for_ns_event (NSEvent *nsevent,
          {
            if (pointer_grab_owner_events)
              {
-               mouse_window = find_window_for_mouse_ns_event (nsevent, x, y);
+                mouse_window = find_mouse_window_for_ns_event (nsevent, x, y);
                event_mask = get_event_mask_from_ns_event (nsevent);
                real_window = find_window_interested_in_event_mask (mouse_window, event_mask, TRUE);
                
@@ -1116,7 +1127,7 @@ find_window_for_ns_event (NSEvent *nsevent,
        else 
          {
            /* The non-grabbed case. */
-           mouse_window = find_window_for_mouse_ns_event (nsevent, x, y);
+            mouse_window = find_mouse_window_for_ns_event (nsevent, x, y);
            event_mask = get_event_mask_from_ns_event (nsevent);
            real_window = find_window_interested_in_event_mask (mouse_window, event_mask, TRUE);
            
@@ -1136,15 +1147,90 @@ find_window_for_ns_event (NSEvent *nsevent,
       
     case NSMouseEntered:
       {
-       GdkWindow *mouse_window;
+       GdkWindow *event_toplevel;
+        GdkWindow *mouse_window;
+        NSPoint point;
+        gint x_tmp, y_tmp;
+
+        event_toplevel = [(GdkQuartzView *)[[nsevent window] contentView] gdkWindow];
+        point = [nsevent locationInWindow];
+
+        x_tmp = point.x;
+
+        /* Flip the y coordinate. */
+        if (event_toplevel == _gdk_root)
+          y_tmp = _gdk_quartz_window_get_inverted_screen_y (point.y);
+        else
+          {
+            GdkWindowImplQuartz *impl;
+            
+            impl = GDK_WINDOW_IMPL_QUARTZ (GDK_WINDOW_OBJECT (event_toplevel)->impl);
+            y_tmp = impl->height - point.y;
+          }
+
+        /* This is the only case where we actually use the window from
+         * the event since we need to know which toplevel we entered
+         * so it can be tracked properly.
+         */
+       mouse_window = _gdk_quartz_window_find_child (event_toplevel, x_tmp, y_tmp);
+
+        /* Treat unknown windows (including title bar/buttons,
+         * desktop) as the root.
+         */
+        if (!mouse_window) 
+          mouse_window = _gdk_root;
 
-       mouse_window = find_window_for_mouse_ns_event (nsevent, x, y);
        synthesize_crossing_events (mouse_window, GDK_CROSSING_NORMAL, nsevent, *x, *y);
       }
       break;
 
     case NSMouseExited:
-      synthesize_crossing_events (_gdk_root, GDK_CROSSING_NORMAL, nsevent, *x, *y);
+      {
+       GdkWindow *event_toplevel;
+        GdkWindow *mouse_window;
+        NSPoint point;
+        gint x_tmp, y_tmp;
+        gint x_orig, y_orig;
+
+        /* We get mouse exited when leaving toplevels. We only use
+         * this when leaving from a window to the root window. The
+         * other case is handled above by checking the motion/button
+         * events, or getting a MouseEntered for another GDK window.
+         *
+         * The reason we don't use MouseExited for other windows is
+         * that quartz first delivers the entered event and then the
+         * exited which is the opposite from what we need.
+         */
+
+        /* Check if the root window has a child at this position, if
+         * so ignore the event.
+         */
+        event_toplevel = [(GdkQuartzView *)[[nsevent window] contentView] gdkWindow];
+        point = [nsevent locationInWindow];
+
+        x_tmp = point.x;
+
+        /* Flip the y coordinate. */
+        if (event_toplevel == _gdk_root)
+          y_tmp = _gdk_quartz_window_get_inverted_screen_y (point.y);
+        else
+          {
+            GdkWindowImplQuartz *impl;
+            
+            impl = GDK_WINDOW_IMPL_QUARTZ (GDK_WINDOW_OBJECT (event_toplevel)->impl);
+            y_tmp = impl->height - point.y;
+          }
+
+        if (gdk_window_get_origin (event_toplevel, &x_orig, &y_orig))
+          {
+            x_tmp += x_orig;
+            y_tmp += y_orig;
+          }
+
+        mouse_window = _gdk_quartz_window_find_child (_gdk_root, x_tmp, y_tmp);
+        if (mouse_window == _gdk_root)
+          synthesize_crossing_events (_gdk_root, GDK_CROSSING_NORMAL, nsevent, *x, *y);
+      }
       break;
 
     case NSKeyDown: